package com.reliable.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class JsObfuscatorUtil {

    private static final Logger logger = LoggerFactory.getLogger(JsObfuscatorUtil.class);

    // 使用 npx 调用 javascript-obfuscator（推荐方式，无需本地 node_modules）
    private static final String OBFUSCATOR_CMD = "npx";

    /**
     * 使用外部 javascript-obfuscator 工具混淆 JS 代码。
     *
     * @param jsCode 原始 JavaScript 代码
     * @return 混淆后的 JavaScript 代码，如果混淆失败则返回原始代码。
     */
    public static String obfuscateWithExternalTool(String jsCode) {
        if (jsCode == null || jsCode.trim().isEmpty()) {
            logger.warn("输入的 JS 代码为空，无需混淆。");
            return jsCode;
        }

        Path tempInputFile = null;
        Path tempOutputFile = null;
        try {
            // 1. 创建临时输入文件
            tempInputFile = Files.createTempFile("js_obf_input_", ".js");
            Files.write(tempInputFile, jsCode.getBytes());

            // 2. 定义临时输出文件
            tempOutputFile = Paths.get(tempInputFile.toString() + ".obfuscated.js");

            // 3. 构建命令：使用 npx 运行 javascript-obfuscator
            List<String> command = new ArrayList<>();
            command.add(OBFUSCATOR_CMD);
            command.add("javascript-obfuscator"); // 包名，npx 会自动下载或使用已缓存的版本
            command.add(tempInputFile.toString());
            command.add("--output");
            command.add(tempOutputFile.toString());

            // --- 混淆选项配置 ---
            command.add("--compact");
            command.add("true");

            command.add("--control-flow-flattening");
            command.add("true");

            command.add("--dead-code-injection");
            command.add("true");

            command.add("--self-defending");
            command.add("true");

            command.add("--string-array-wrappers-chained-calls");
            command.add("true");

            command.add("--split-strings");
            command.add("true");

            // 可选：禁用 console 输出（更安全）
            command.add("--disable-console-output");
            command.add("true");

            // 可选：字符串数组混淆（增强）
            // command.add("--string-array");
            // command.add("true");

            // 可选：调试保护（谨慎使用，可能导致页面卡死）
            // command.add("--debug-protection");
            // command.add("false");

            logger.debug("执行混淆命令: {}", String.join(" ", command));

            // 4. 启动进程
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            processBuilder.redirectErrorStream(true); // 合并标准输出和错误输出
            Process process = processBuilder.start();

            // 5. 读取并记录混淆器输出（用于调试）
            StringBuilder processOutput = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    processOutput.append(line).append("\n");
                }
            }
            if (processOutput.length() > 0) {
                logger.debug("混淆器输出/日志:\n{}", processOutput.toString());
            }

            // 6. 等待完成（设置超时）
            boolean finished = process.waitFor(60, TimeUnit.SECONDS);
            int exitCode = process.exitValue();
            if (!finished) {
                logger.error("混淆器进程执行超时（60秒）");
                process.destroyForcibly();
                return jsCode;
            }
            if (exitCode != 0) {
                logger.error("混淆器执行失败，退出码: {}, 输出日志:\n{}", exitCode, processOutput.toString());
                logger.error("执行的完整命令: {}", String.join(" ", command));
                return jsCode;
            }

            // 7. 读取混淆后的代码
            if (Files.exists(tempOutputFile)) {
                byte[] bytes = Files.readAllBytes(tempOutputFile);
                String obfuscatedCode = new String(bytes);
                logger.debug("JS 代码混淆成功，输出长度: {} 字符", obfuscatedCode.length());
                return obfuscatedCode;
            } else {
                logger.error("混淆器未生成输出文件: {}", tempOutputFile.toAbsolutePath());
                return jsCode;
            }

        } catch (Exception e) {
            logger.error("混淆 JS 代码时发生异常", e);
            return jsCode;
        } finally {
            // 8. 清理临时文件
            try {
                if (tempInputFile != null && Files.exists(tempInputFile)) {
                    Files.delete(tempInputFile);
                }
            } catch (IOException e) {
                logger.warn("删除临时输入文件失败: {}", tempInputFile, e);
            }
            try {
                if (tempOutputFile != null && Files.exists(tempOutputFile)) {
                    Files.delete(tempOutputFile);
                }
            } catch (IOException e) {
                logger.warn("删除临时输出文件失败: {}", tempOutputFile, e);
            }
        }
    }

    // ✅ 测试用 main 方法（可选）
    // public static void main(String[] args) {
    //     String testJs = "function hello(name) { console.log('Hello, ' + name + '!'); var secret = 'This is a secret'; } hello('World');";
    //     System.out.println("原始 JS:");
    //     System.out.println(testJs);
    //     System.out.println("\n正在混淆...");
    //     String obfuscated = JsObfuscatorUtil.obfuscateWithExternalTool(testJs);
    //     System.out.println("\n混淆后 JS:");
    //     System.out.println(obfuscated);
    // }
}